/***************************************************************************
 *
 * This file is provided under a dual BSD/GPLv2 license.  When using or
 *   redistributing this file, you may do so under either license.
 * 
 *   GPL LICENSE SUMMARY
 * 
 *   Copyright(c) 2007-2022 Intel Corporation. All rights reserved.
 * 
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of version 2 of the GNU General Public License as
 *   published by the Free Software Foundation.
 * 
 *   This program is distributed in the hope that it will be useful, but
 *   WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   General Public License for more details.
 * 
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *   The full GNU General Public License is included in this distribution
 *   in the file called LICENSE.GPL.
 * 
 *   Contact Information:
 *   Intel Corporation
 * 
 *   BSD LICENSE
 * 
 *   Copyright(c) 2007-2022 Intel Corporation. All rights reserved.
 *   All rights reserved.
 * 
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 * 
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 * 
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * 
 *
 ***************************************************************************/

/**
 ***************************************************************************
 * @file lac_sym_qat_hash.c
 *
 * @ingroup LacSymQatHash
 *
 * Implementation for populating QAT data structures for hash operation
 ***************************************************************************/

/*
*******************************************************************************
* Include public/global header files
*******************************************************************************
*/

#include "cpa.h"
#include "cpa_cy_sym.h"
#include "icp_accel_devices.h"
#include "icp_adf_debug.h"
#include "lac_mem.h"
#include "lac_sym.h"
#include "lac_common.h"
#include "lac_sym_qat.h"
#include "lac_list.h"
#include "lac_sal_types.h"
#include "lac_sal_types_crypto.h"
#include "lac_sym_qat_hash.h"
#include "lac_sym_qat_hash_defs_lookup.h"

/**
 * This structure contains pointers into the hash setup block of the
 * security descriptor. As the hash setup block contains fields that
 * are of variable length, pointers must be calculated to these fields
 * and the hash setup block is populated using these pointers. */
typedef struct lac_hash_blk_ptrs_s
{
    icp_qat_hw_auth_setup_t *pInHashSetup;
    /**< inner hash setup */
    Cpa8U *pInHashInitState1;
    /**< inner initial state 1 */
    Cpa8U *pInHashInitState2;
    /**< inner initial state 2 */
    icp_qat_hw_auth_setup_t *pOutHashSetup;
    /**< outer hash setup */
    Cpa8U *pOutHashInitState1;
    /**< outer hash initial state */
} lac_hash_blk_ptrs_t;

typedef struct lac_hash_blk_ptrs_optimised_s
{
    Cpa8U *pInHashInitState1;
    /**< inner initial state 1 */
    Cpa8U *pInHashInitState2;
    /**< inner initial state 2 */

} lac_hash_blk_ptrs_optimised_t;

/**
 * This function calculates the pointers into the hash setup block
 * based on the control block
 *
 * @param[in]  pHashControlBlock    Pointer to hash control block
 * @param[in]  pHwBlockBase         pointer to base of hardware block
 * @param[out] pHashBlkPtrs         structure containing pointers to
 *                                  various fields in the hash setup block
 *
 * @return void
 */
STATIC void LacSymQat_HashHwBlockPtrsInit(
    icp_qat_fw_auth_cd_ctrl_hdr_t *pHashControlBlock,
    void *pHwBlockBase,
    lac_hash_blk_ptrs_t *pHashBlkPtrs);

STATIC void LacSymQat_HashSetupBlockOptimisedFormatInit(
    const CpaCySymHashSetupData *pHashSetupData,
    icp_qat_fw_auth_cd_ctrl_hdr_t *pHashControlBlock,
    void *pHwBlockBase,
    icp_qat_hw_auth_mode_t qatHashMode,
    lac_sym_qat_hash_precompute_info_t *pPrecompute,
    lac_sym_qat_hash_defs_t *pHashDefs,
    lac_sym_qat_hash_defs_t *pOuterHashDefs);

/**
 * This function populates the hash setup block
 *
 * @param[in]  pHashSetupData             Pointer to the hash context
 * @param[in]  pHashControlBlock    Pointer to hash control block
 * @param[in]  pHwBlockBase         pointer to base of hardware block
 * @param[in]  qatHashMode          QAT hash mode
 * @param[in]  pPrecompute          For auth mode, this is the pointer
 *                                  to the precompute data. Otherwise this
 *                                  should be set to NULL
 * @param[in]  pHashDefs            Pointer to Hash definitions
 * @param[in]  pOuterHashDefs       Pointer to Outer Hash definitions.
 *                                  Required for nested hash mode only
 * @param[in]  instanceHandle       Instance Handle
 *
 * @return void
 */
STATIC void LacSymQat_HashSetupBlockInit(
    const CpaCySymHashSetupData *pHashSetupData,
    icp_qat_fw_auth_cd_ctrl_hdr_t *pHashControlBlock,
    void *pHwBlockBase,
    icp_qat_hw_auth_mode_t qatHashMode,
    lac_sym_qat_hash_precompute_info_t *pPrecompute,
    lac_sym_qat_hash_defs_t *pHashDefs,
    lac_sym_qat_hash_defs_t *pOuterHashDefs,
    CpaInstanceHandle instanceHandle);

/** @ingroup LacSymQatHash */
void LacSymQat_HashGetCfgData(CpaInstanceHandle pInstance,
                              icp_qat_hw_auth_mode_t qatHashMode,
                              CpaCySymHashMode apiHashMode,
                              CpaCySymHashAlgorithm apiHashAlgorithm,
                              icp_qat_hw_auth_algo_t *pQatAlgorithm,
                              CpaBoolean *pQatNested,
                              Cpa32U authKeyLenInBytes,
                              Cpa32U digestResultLenInBytes)
{
    lac_sym_qat_hash_defs_t *pHashDefs = NULL;

    LAC_ENSURE_NOT_NULL(pInstance);
    LAC_ENSURE_NOT_NULL(pQatAlgorithm);
    LAC_ENSURE_NOT_NULL(pQatNested);

    LacSymQat_HashDefsLookupGet(pInstance,
                                apiHashAlgorithm,
                                &pHashDefs,
                                authKeyLenInBytes,
                                digestResultLenInBytes);

    LAC_ENSURE_NOT_NULL(pHashDefs);
    *pQatAlgorithm = pHashDefs->qatInfo->algoEnc;

    if (IS_HASH_MODE_2(qatHashMode))
    {
        /* set bit for nested hashing */
        *pQatNested = ICP_QAT_FW_AUTH_HDR_FLAG_DO_NESTED;
    }
    /* Nested hash in mode 0. */
    else if (CPA_CY_SYM_HASH_MODE_NESTED == apiHashMode)
    {
        /* set bit for nested hashing */
        *pQatNested = ICP_QAT_FW_AUTH_HDR_FLAG_DO_NESTED;
    }
    /* mode0 - plain or mode1 - auth */
    else
    {
        *pQatNested = ICP_QAT_FW_AUTH_HDR_FLAG_NO_NESTED;
    }
}

/** @ingroup LacSymQatHash */
void LacSymQat_HashContentDescInit(
    icp_qat_la_bulk_req_ftr_t *pMsg,
    CpaInstanceHandle instanceHandle,
    const CpaCySymHashSetupData *pHashSetupData,
    void *pHwBlockBase,
    Cpa32U hwBlockOffsetInQuadWords,
    icp_qat_fw_slice_t nextSlice,
    icp_qat_hw_auth_mode_t qatHashMode,
    CpaBoolean useSymConstantsTable,
    CpaBoolean useOptimisedContentDesc,
    lac_sym_qat_hash_precompute_info_t *pPrecompute,
    Cpa32U *pHashBlkSizeInBytes)
{
    icp_qat_fw_auth_cd_ctrl_hdr_t *cd_ctrl =
        (icp_qat_fw_auth_cd_ctrl_hdr_t *)&(pMsg->cd_ctrl);
    lac_sym_qat_hash_defs_t *pHashDefs = NULL;
    lac_sym_qat_hash_defs_t *pOuterHashDefs = NULL;
    Cpa32U hashSetupBlkSize = 0;
    Cpa32U authKeyLenInBytes =
        pHashSetupData->authModeSetupData.authKeyLenInBytes;
    Cpa32U digestResultLenInBytes = pHashSetupData->digestResultLenInBytes;

    LAC_ENSURE_NOT_NULL(pHashSetupData);
    LAC_ENSURE_NOT_NULL(pHwBlockBase);
    LAC_ENSURE_NOT_NULL(pHashBlkSizeInBytes);

    LacSymQat_HashDefsLookupGet(instanceHandle,
                                pHashSetupData->hashAlgorithm,
                                &pHashDefs,
                                authKeyLenInBytes,
                                digestResultLenInBytes);

    LAC_ENSURE_NOT_NULL(pHashDefs);

    /* setup the offset in QuadWords into the HW block */
    cd_ctrl->hash_cfg_offset = (Cpa8U)hwBlockOffsetInQuadWords;
    ICP_QAT_FW_COMN_NEXT_ID_SET(cd_ctrl, nextSlice);
    ICP_QAT_FW_COMN_CURR_ID_SET(cd_ctrl, ICP_QAT_FW_SLICE_AUTH);

    /* Hmac in mode 2 TLS */
    if (IS_HASH_MODE_2(qatHashMode))
    {
            /* set bit for HMAC mode2 */
            ICP_QAT_FW_HASH_FLAG_MODE2_SET(cd_ctrl->hash_flags,
                                           QAT_FW_LA_MODE2);
    }
    /* Nested hash in mode 0 */
    else if (CPA_CY_SYM_HASH_MODE_NESTED == pHashSetupData->hashMode)
    {
        /* Set bit for nested hashing.
         * Make sure not to overwrite other flags in hash_flags byte.
         */
        ICP_QAT_FW_HASH_FLAG_AUTH_HDR_NESTED_SET(
            cd_ctrl->hash_flags, ICP_QAT_FW_AUTH_HDR_FLAG_DO_NESTED);
    }
    /* mode0 - plain or mode1 - auth */
    else
    {
        ICP_QAT_FW_HASH_FLAG_AUTH_HDR_NESTED_SET(
            cd_ctrl->hash_flags, ICP_QAT_FW_AUTH_HDR_FLAG_NO_NESTED);
    }

    /* set the final digest size */
    cd_ctrl->final_sz = (Cpa8U)pHashSetupData->digestResultLenInBytes;

    /* set the state1 size */
    cd_ctrl->inner_state1_sz = LAC_ALIGN_POW2_ROUNDUP(
        pHashDefs->qatInfo->state1Length, LAC_QUAD_WORD_IN_BYTES);

    /* set the inner result size to the digest length */
    cd_ctrl->inner_res_sz = (Cpa8U)pHashDefs->algInfo->digestLength;

    /* set the state2 size - only for mode 1 Auth algos and AES CBC MAC */
    if (IS_HASH_MODE_1(qatHashMode) ||
        pHashSetupData->hashAlgorithm == CPA_CY_SYM_HASH_AES_CBC_MAC ||
        pHashSetupData->hashAlgorithm == CPA_CY_SYM_HASH_ZUC_EIA3)
    {
        cd_ctrl->inner_state2_sz = LAC_ALIGN_POW2_ROUNDUP(
            pHashDefs->qatInfo->state2Length, LAC_QUAD_WORD_IN_BYTES);
    }
    else
    {
        cd_ctrl->inner_state2_sz = 0;
    }

    if (useSymConstantsTable)
    {
        cd_ctrl->inner_state2_offset =
            LAC_BYTES_TO_QUADWORDS(cd_ctrl->inner_state1_sz);

        /* size of inner part of hash setup block */
        hashSetupBlkSize = cd_ctrl->inner_state1_sz + cd_ctrl->inner_state2_sz;
    }
    else
    {
        cd_ctrl->inner_state2_offset =
            cd_ctrl->hash_cfg_offset +
            LAC_BYTES_TO_QUADWORDS(sizeof(icp_qat_hw_auth_setup_t) +
                                   cd_ctrl->inner_state1_sz);

        /* size of inner part of hash setup block */
        hashSetupBlkSize = sizeof(icp_qat_hw_auth_setup_t) +
                           cd_ctrl->inner_state1_sz + cd_ctrl->inner_state2_sz;
    }

    /* For nested hashing - Fill in the outer fields */
    if (CPA_CY_SYM_HASH_MODE_NESTED == pHashSetupData->hashMode ||
        IS_HASH_MODE_2(qatHashMode))
    {
        /* For nested - use the outer algorithm. This covers TLS and
         * nested hash. For HMAC mode2 use inner algorithm again */
        CpaCySymHashAlgorithm outerAlg =
            (CPA_CY_SYM_HASH_MODE_NESTED == pHashSetupData->hashMode)
                ? pHashSetupData->nestedModeSetupData.outerHashAlgorithm
                : pHashSetupData->hashAlgorithm;

        LacSymQat_HashDefsLookupGet(instanceHandle,
                                    outerAlg,
                                    &pOuterHashDefs,
                                    authKeyLenInBytes,
                                    digestResultLenInBytes);

        LAC_ENSURE_NOT_NULL(pOuterHashDefs);

        /* outer config offset */
        cd_ctrl->outer_config_offset =
            cd_ctrl->inner_state2_offset +
            LAC_BYTES_TO_QUADWORDS(cd_ctrl->inner_state2_sz);

        cd_ctrl->outer_state1_sz = LAC_ALIGN_POW2_ROUNDUP(
            pOuterHashDefs->algInfo->stateSize, LAC_QUAD_WORD_IN_BYTES);

        /* outer result size */
        cd_ctrl->outer_res_sz = (Cpa8U)pOuterHashDefs->algInfo->digestLength;

        /* outer_prefix_offset will be the size of the inner prefix data
         * plus the hash state storage size. */
        /* The prefix buffer is part of the ReqParams, so this param will be
         * setup where ReqParams are set up */

        /* add on size of outer part of hash block */
        hashSetupBlkSize +=
            sizeof(icp_qat_hw_auth_setup_t) + cd_ctrl->outer_state1_sz;
    }
    else
    {
        cd_ctrl->outer_config_offset = 0;
        cd_ctrl->outer_state1_sz = 0;
        cd_ctrl->outer_res_sz = 0;
    }

    if (CPA_CY_SYM_HASH_SNOW3G_UIA2 == pHashSetupData->hashAlgorithm)
    {
        /* add the size for the cipher config word, the key and the IV*/
        hashSetupBlkSize +=
            sizeof(icp_qat_hw_cipher_config_t) +
            pHashSetupData->authModeSetupData.authKeyLenInBytes +
            ICP_QAT_HW_SNOW_3G_UEA2_IV_SZ;
    }

    *pHashBlkSizeInBytes = hashSetupBlkSize;

    if (useOptimisedContentDesc)
    {
        LAC_LOG_DEBUG("Hash HwSetupBlock in DRAM optimised CD");
        LacSymQat_HashSetupBlockOptimisedFormatInit(pHashSetupData,
                                                    cd_ctrl,
                                                    pHwBlockBase,
                                                    qatHashMode,
                                                    pPrecompute,
                                                    pHashDefs,
                                                    pOuterHashDefs);
    }
    else if (!useSymConstantsTable)
    {
        /*****************************************************************************
         *                        Populate Hash Setup block *
         *****************************************************************************/
        LacSymQat_HashSetupBlockInit(pHashSetupData,
                                     cd_ctrl,
                                     pHwBlockBase,
                                     qatHashMode,
                                     pPrecompute,
                                     pHashDefs,
                                     pOuterHashDefs,
                                     instanceHandle);
        LAC_LOG_DEBUG("Hash HwSetupBlock in DRAM");
    }
}

/* This fn populates fields in both the CD ctrl block and the ReqParams block
 * which describe the Hash ReqParams:
 * cd_ctrl.outer_prefix_offset
 * cd_ctrl.outer_prefix_sz
 * req_params.inner_prefix_sz/aad_sz
 * req_params.hash_state_sz
 * req_params.auth_res_sz
 *
 */
void LacSymQat_HashSetupReqParamsMetaData(
    icp_qat_la_bulk_req_ftr_t *pMsg,
    CpaInstanceHandle instanceHandle,
    const CpaCySymHashSetupData *pHashSetupData,
    CpaBoolean hashStateBuffer,
    icp_qat_hw_auth_mode_t qatHashMode,
    CpaBoolean digestVerify)
{
    icp_qat_fw_auth_cd_ctrl_hdr_t *cd_ctrl = NULL;
    icp_qat_la_auth_req_params_t *pHashReqParams = NULL;
    lac_sym_qat_hash_defs_t *pHashDefs = NULL;
    Cpa32U authKeyLenInBytes =
        pHashSetupData->authModeSetupData.authKeyLenInBytes;
    Cpa32U digestResultLenInBytes = pHashSetupData->digestResultLenInBytes;

    LAC_ENSURE_NOT_NULL(pMsg);
    LAC_ENSURE_NOT_NULL(pHashSetupData);

    cd_ctrl = (icp_qat_fw_auth_cd_ctrl_hdr_t *)&(pMsg->cd_ctrl);
    pHashReqParams =
        (icp_qat_la_auth_req_params_t *)(&(pMsg->serv_specif_rqpars));

    LacSymQat_HashDefsLookupGet(instanceHandle,
                                pHashSetupData->hashAlgorithm,
                                &pHashDefs,
                                authKeyLenInBytes,
                                digestResultLenInBytes);

    LAC_ENSURE_NOT_NULL(pHashDefs);
    /* Hmac in mode 2 TLS */
    if (IS_HASH_MODE_2(qatHashMode))
    {
        /* Inner and outer prefixes are the block length */
        pHashReqParams->u2.inner_prefix_sz =
            (Cpa8U)pHashDefs->algInfo->blockLength;
        cd_ctrl->outer_prefix_sz = (Cpa8U)pHashDefs->algInfo->blockLength;
        cd_ctrl->outer_prefix_offset =
            LAC_BYTES_TO_QUADWORDS(LAC_ALIGN_POW2_ROUNDUP(
                (pHashReqParams->u2.inner_prefix_sz), LAC_QUAD_WORD_IN_BYTES));
    }
    /* Nested hash in mode 0 */
    else if (CPA_CY_SYM_HASH_MODE_NESTED == pHashSetupData->hashMode)
    {

        /* set inner and outer prefixes */
        pHashReqParams->u2.inner_prefix_sz =
            (Cpa8U)pHashSetupData->nestedModeSetupData.innerPrefixLenInBytes;
        cd_ctrl->outer_prefix_sz =
            (Cpa8U)pHashSetupData->nestedModeSetupData.outerPrefixLenInBytes;
        cd_ctrl->outer_prefix_offset =
            LAC_BYTES_TO_QUADWORDS(LAC_ALIGN_POW2_ROUNDUP(
                (pHashReqParams->u2.inner_prefix_sz), LAC_QUAD_WORD_IN_BYTES));
    }
    /* mode0 - plain or mode1 - auth */
    else
    {
        Cpa16U aadDataSize = 0;

        /* For Auth Encrypt set the aad size */
        if (CPA_CY_SYM_HASH_AES_CCM == pHashSetupData->hashAlgorithm)
        {
            /* at the beginning of the buffer there is B0 block */
            aadDataSize = LAC_HASH_AES_CCM_BLOCK_SIZE;

            /* then, if there is some 'a' data, the buffer will store encoded
             * length of 'a' and 'a' itself */
            if (pHashSetupData->authModeSetupData.aadLenInBytes > 0)
            {
                /* as the QAT API puts the requirement on the
                 * pAdditionalAuthData not to be bigger than 240 bytes then we
                 * just need 2 bytes to store encoded length of 'a' */
                aadDataSize += sizeof(Cpa16U);
                aadDataSize +=
                    (Cpa16U)pHashSetupData->authModeSetupData.aadLenInBytes;
            }

            /* round the aad size to the multiple of CCM block size.*/
            pHashReqParams->u2.aad_sz = LAC_ALIGN_POW2_ROUNDUP(
                aadDataSize, LAC_HASH_AES_CCM_BLOCK_SIZE);
        }
        else if (CPA_CY_SYM_HASH_AES_GCM == pHashSetupData->hashAlgorithm)
        {
            aadDataSize =
                (Cpa16U)pHashSetupData->authModeSetupData.aadLenInBytes;

            /* round the aad size to the multiple of GCM hash block size. */
            pHashReqParams->u2.aad_sz = LAC_ALIGN_POW2_ROUNDUP(
                aadDataSize, LAC_HASH_AES_GCM_BLOCK_SIZE);
        }
        else
        {
            pHashReqParams->u2.aad_sz = 0;
        }

        cd_ctrl->outer_prefix_sz = 0;
        cd_ctrl->outer_prefix_offset = 0;
    }

    /* If there is a hash state prefix buffer */
    if (CPA_TRUE == hashStateBuffer)
    {
        /* Note, this sets up size for both aad and non-aad cases */
        pHashReqParams->hash_state_sz = LAC_BYTES_TO_QUADWORDS(
            LAC_ALIGN_POW2_ROUNDUP(pHashReqParams->u2.inner_prefix_sz,
                                   LAC_QUAD_WORD_IN_BYTES) +
            LAC_ALIGN_POW2_ROUNDUP(cd_ctrl->outer_prefix_sz,
                                   LAC_QUAD_WORD_IN_BYTES));
    }
    else
    {
        pHashReqParams->hash_state_sz = 0;
    }

    if (CPA_TRUE == digestVerify)
    {
        /* auth result size in bytes to be read in for a verify operation */
        pHashReqParams->auth_res_sz =
            (Cpa8U)pHashSetupData->digestResultLenInBytes;
    }
    else
    {
        pHashReqParams->auth_res_sz = 0;
    }

    pHashReqParams->resrvd1 = 0;
}

void LacSymQat_HashHwBlockPtrsInit(icp_qat_fw_auth_cd_ctrl_hdr_t *cd_ctrl,
                                   void *pHwBlockBase,
                                   lac_hash_blk_ptrs_t *pHashBlkPtrs)
{
    LAC_ENSURE_NOT_NULL(cd_ctrl);
    LAC_ENSURE_NOT_NULL(pHwBlockBase);
    LAC_ENSURE_NOT_NULL(pHashBlkPtrs);

    /* encoded offset for inner config is converted to a byte offset. */
    pHashBlkPtrs->pInHashSetup =
        (icp_qat_hw_auth_setup_t *)((Cpa8U *)pHwBlockBase +
                                    (cd_ctrl->hash_cfg_offset *
                                     LAC_QUAD_WORD_IN_BYTES));

    pHashBlkPtrs->pInHashInitState1 =
        (Cpa8U *)pHashBlkPtrs->pInHashSetup + sizeof(icp_qat_hw_auth_setup_t);

    pHashBlkPtrs->pInHashInitState2 =
        (Cpa8U *)(pHashBlkPtrs->pInHashInitState1) + cd_ctrl->inner_state1_sz;

    pHashBlkPtrs->pOutHashSetup =
        (icp_qat_hw_auth_setup_t *)((Cpa8U *)(pHashBlkPtrs->pInHashInitState2) +
                                    cd_ctrl->inner_state2_sz);

    pHashBlkPtrs->pOutHashInitState1 = (Cpa8U *)(pHashBlkPtrs->pOutHashSetup) +
                                       sizeof(icp_qat_hw_auth_setup_t);
}

STATIC void LacSymQat_HashSetupBlockInit(
    const CpaCySymHashSetupData *pHashSetupData,
    icp_qat_fw_auth_cd_ctrl_hdr_t *pHashControlBlock,
    void *pHwBlockBase,
    icp_qat_hw_auth_mode_t qatHashMode,
    lac_sym_qat_hash_precompute_info_t *pPrecompute,
    lac_sym_qat_hash_defs_t *pHashDefs,
    lac_sym_qat_hash_defs_t *pOuterHashDefs,
    CpaInstanceHandle instanceHandle)
{
    Cpa32U innerConfig = 0;
    lac_hash_blk_ptrs_t hashBlkPtrs = {0};
    Cpa32U aedHashCmpLength = 0;
    LacSymQat_HashHwBlockPtrsInit(
        pHashControlBlock, pHwBlockBase, &hashBlkPtrs);

    innerConfig =
        ICP_QAT_HW_AUTH_CONFIG_BUILD(qatHashMode,
                                     pHashDefs->qatInfo->algoEnc,
                                     pHashSetupData->digestResultLenInBytes);

    /* Set the Inner hash configuration */
    hashBlkPtrs.pInHashSetup->auth_config.config = innerConfig;
    hashBlkPtrs.pInHashSetup->auth_config.reserved = 0;

    /* For mode 1 pre-computes for auth algorithms */
    if (IS_HASH_MODE_1(qatHashMode) ||
        CPA_CY_SYM_HASH_AES_CBC_MAC == pHashSetupData->hashAlgorithm ||
        CPA_CY_SYM_HASH_ZUC_EIA3 == pHashSetupData->hashAlgorithm)
    {
        /* for HMAC in mode 1 authCounter is the block size
         * else the authCounter is 0. The firmware expects the counter to be
         * big endian */
        LAC_MEM_SHARED_WRITE_SWAP(
            hashBlkPtrs.pInHashSetup->auth_counter.counter,
            pHashDefs->qatInfo->authCounter);

        /* state 1 is set to 0 for the following algorithms */
        if ((CPA_CY_SYM_HASH_AES_XCBC == pHashSetupData->hashAlgorithm) ||
            (CPA_CY_SYM_HASH_AES_CMAC == pHashSetupData->hashAlgorithm) ||
            (CPA_CY_SYM_HASH_AES_CBC_MAC == pHashSetupData->hashAlgorithm) ||
            (CPA_CY_SYM_HASH_KASUMI_F9 == pHashSetupData->hashAlgorithm) ||
            (CPA_CY_SYM_HASH_SNOW3G_UIA2 == pHashSetupData->hashAlgorithm) ||
            (CPA_CY_SYM_HASH_AES_CCM == pHashSetupData->hashAlgorithm) ||
            (CPA_CY_SYM_HASH_AES_GMAC == pHashSetupData->hashAlgorithm) ||
            (CPA_CY_SYM_HASH_AES_GCM == pHashSetupData->hashAlgorithm) ||
            (CPA_CY_SYM_HASH_ZUC_EIA3 == pHashSetupData->hashAlgorithm))
        {
            LAC_OS_BZERO(hashBlkPtrs.pInHashInitState1,
                         pHashDefs->qatInfo->state1Length);
        }

        /* Pad remaining bytes of sha1 precomputes */
        if (CPA_CY_SYM_HASH_SHA1 == pHashSetupData->hashAlgorithm)
        {
            Cpa32U state1PadLen = 0;
            Cpa32U state2PadLen = 0;

            if (pHashControlBlock->inner_state1_sz >
                pHashDefs->algInfo->stateSize)
            {
                state1PadLen = pHashControlBlock->inner_state1_sz -
                               pHashDefs->algInfo->stateSize;
            }

            if (pHashControlBlock->inner_state2_sz >
                pHashDefs->algInfo->stateSize)
            {
                state2PadLen = pHashControlBlock->inner_state2_sz -
                               pHashDefs->algInfo->stateSize;
            }

            if (state1PadLen > 0)
            {

                LAC_OS_BZERO(hashBlkPtrs.pInHashInitState1 +
                                 pHashDefs->algInfo->stateSize,
                             state1PadLen);
            }

            if (state2PadLen > 0)
            {
                LAC_OS_BZERO(hashBlkPtrs.pInHashInitState2 +
                                 pHashDefs->algInfo->stateSize,
                             state2PadLen);
            }
        }

        LAC_ENSURE_NOT_NULL(pPrecompute);

        if (NULL != pPrecompute)
        {
            pPrecompute->state1Size = pHashDefs->qatInfo->state1Length;
            pPrecompute->state2Size = pHashDefs->qatInfo->state2Length;

            /* Set the destination for pre-compute state1 data to be written */
            pPrecompute->pState1 = hashBlkPtrs.pInHashInitState1;

            /* Set the destination for pre-compute state1 data to be written */
            pPrecompute->pState2 = hashBlkPtrs.pInHashInitState2;
        }
    }
    /* For digest and nested digest */
    else
    {
        Cpa32U padLen =
            pHashControlBlock->inner_state1_sz - pHashDefs->algInfo->stateSize;

        /* counter set to 0 */
        hashBlkPtrs.pInHashSetup->auth_counter.counter = 0;

        /* set the inner hash state 1 */
        memcpy(hashBlkPtrs.pInHashInitState1,
               pHashDefs->algInfo->initState,
               pHashDefs->algInfo->stateSize);

        if (padLen > 0)
        {
            LAC_OS_BZERO(hashBlkPtrs.pInHashInitState1 +
                             pHashDefs->algInfo->stateSize,
                         padLen);
        }
    }

    hashBlkPtrs.pInHashSetup->auth_counter.reserved = 0;

    /* Fill in the outer part of the hash setup block */
    if ((CPA_CY_SYM_HASH_MODE_NESTED == pHashSetupData->hashMode ||
         IS_HASH_MODE_2(qatHashMode)) &&
        (NULL != pOuterHashDefs))
    {
        Cpa32U outerConfig = ICP_QAT_HW_AUTH_CONFIG_BUILD(
            qatHashMode,
            pOuterHashDefs->qatInfo->algoEnc,
            pHashSetupData->digestResultLenInBytes);

        Cpa32U padLen = pHashControlBlock->outer_state1_sz -
                        pOuterHashDefs->algInfo->stateSize;

        /* populate the auth config */
        hashBlkPtrs.pOutHashSetup->auth_config.config = outerConfig;
        hashBlkPtrs.pOutHashSetup->auth_config.reserved = 0;

        /* outer Counter set to 0 */
        hashBlkPtrs.pOutHashSetup->auth_counter.counter = 0;
        hashBlkPtrs.pOutHashSetup->auth_counter.reserved = 0;

        /* set outer hash state 1 */
        memcpy(hashBlkPtrs.pOutHashInitState1,
               pOuterHashDefs->algInfo->initState,
               pOuterHashDefs->algInfo->stateSize);

        if (padLen > 0)
        {
            LAC_OS_BZERO(hashBlkPtrs.pOutHashInitState1 +
                             pOuterHashDefs->algInfo->stateSize,
                         padLen);
        }
    }

    if (CPA_CY_SYM_HASH_SNOW3G_UIA2 == pHashSetupData->hashAlgorithm)
    {
        icp_qat_hw_cipher_config_t *pCipherConfig =
            (icp_qat_hw_cipher_config_t *)hashBlkPtrs.pOutHashSetup;

        pCipherConfig->val =
            ICP_QAT_HW_CIPHER_CONFIG_BUILD(ICP_QAT_HW_CIPHER_ECB_MODE,
                                           ICP_QAT_HW_CIPHER_ALGO_SNOW_3G_UEA2,
                                           ICP_QAT_HW_CIPHER_KEY_CONVERT,
                                           ICP_QAT_HW_CIPHER_ENCRYPT,
                                           aedHashCmpLength);

        pCipherConfig->reserved = 0;

        memcpy((Cpa8U *)pCipherConfig + sizeof(icp_qat_hw_cipher_config_t),
               pHashSetupData->authModeSetupData.authKey,
               pHashSetupData->authModeSetupData.authKeyLenInBytes);

        LAC_OS_BZERO((Cpa8U *)pCipherConfig +
                         sizeof(icp_qat_hw_cipher_config_t) +
                         pHashSetupData->authModeSetupData.authKeyLenInBytes,
                     ICP_QAT_HW_SNOW_3G_UEA2_IV_SZ);
    }
    else if (CPA_CY_SYM_HASH_ZUC_EIA3 == pHashSetupData->hashAlgorithm)
    {
        icp_qat_hw_cipher_config_t *pCipherConfig =
            (icp_qat_hw_cipher_config_t *)hashBlkPtrs.pOutHashSetup;

        pCipherConfig->val = ICP_QAT_HW_CIPHER_CONFIG_BUILD(
            ICP_QAT_HW_CIPHER_ECB_MODE,
            ICP_QAT_HW_CIPHER_ALGO_ZUC_3G_128_EEA3,
            ICP_QAT_HW_CIPHER_KEY_CONVERT,
            ICP_QAT_HW_CIPHER_ENCRYPT,
            aedHashCmpLength);

        pCipherConfig->reserved = 0;

        memcpy((Cpa8U *)pCipherConfig + sizeof(icp_qat_hw_cipher_config_t),
               pHashSetupData->authModeSetupData.authKey,
               pHashSetupData->authModeSetupData.authKeyLenInBytes);

        LAC_OS_BZERO((Cpa8U *)pCipherConfig +
                         sizeof(icp_qat_hw_cipher_config_t) +
                         pHashSetupData->authModeSetupData.authKeyLenInBytes,
                     ICP_QAT_HW_ZUC_3G_EEA3_IV_SZ);
    }
}

void LacSymQat_HashOpHwBlockPtrsInit(
    icp_qat_fw_auth_cd_ctrl_hdr_t *cd_ctrl,
    void *pHwBlockBase,
    lac_hash_blk_ptrs_optimised_t *pHashBlkPtrs)
{
    LAC_ENSURE_NOT_NULL(cd_ctrl);
    LAC_ENSURE_NOT_NULL(pHwBlockBase);
    LAC_ENSURE_NOT_NULL(pHashBlkPtrs);
    pHashBlkPtrs->pInHashInitState1 = (((Cpa8U *)pHwBlockBase) + 16);
    pHashBlkPtrs->pInHashInitState2 =
        (Cpa8U *)(pHashBlkPtrs->pInHashInitState1) + cd_ctrl->inner_state1_sz;
}

STATIC void LacSymQat_HashSetupBlockOptimisedFormatInit(
    const CpaCySymHashSetupData *pHashSetupData,
    icp_qat_fw_auth_cd_ctrl_hdr_t *pHashControlBlock,
    void *pHwBlockBase,
    icp_qat_hw_auth_mode_t qatHashMode,
    lac_sym_qat_hash_precompute_info_t *pPrecompute,
    lac_sym_qat_hash_defs_t *pHashDefs,
    lac_sym_qat_hash_defs_t *pOuterHashDefs)
{

    Cpa32U state1PadLen = 0;
    Cpa32U state2PadLen = 0;

    lac_hash_blk_ptrs_optimised_t pHashBlkPtrs = {0};

    LacSymQat_HashOpHwBlockPtrsInit(
        pHashControlBlock, pHwBlockBase, &pHashBlkPtrs);

    if (pHashControlBlock->inner_state1_sz > pHashDefs->algInfo->stateSize)
    {
        state1PadLen =
            pHashControlBlock->inner_state1_sz - pHashDefs->algInfo->stateSize;
    }

    if (pHashControlBlock->inner_state2_sz > pHashDefs->algInfo->stateSize)
    {
        state2PadLen =
            pHashControlBlock->inner_state2_sz - pHashDefs->algInfo->stateSize;
    }

    if (state1PadLen > 0)
    {

        LAC_OS_BZERO(pHashBlkPtrs.pInHashInitState1 +
                         pHashDefs->algInfo->stateSize,
                     state1PadLen);
    }

    if (state2PadLen > 0)
    {

        LAC_OS_BZERO(pHashBlkPtrs.pInHashInitState2 +
                         pHashDefs->algInfo->stateSize,
                     state2PadLen);
    }

    LAC_ENSURE_NOT_NULL(pPrecompute);

    if (NULL != pPrecompute)
    {
        pPrecompute->state1Size = pHashDefs->qatInfo->state1Length;
        pPrecompute->state2Size = pHashDefs->qatInfo->state2Length;

        /* Set the destination for pre-compute state1 data to be written */
        pPrecompute->pState1 = pHashBlkPtrs.pInHashInitState1;

        /* Set the destination for pre-compute state1 data to be written */
        pPrecompute->pState2 = pHashBlkPtrs.pInHashInitState2;
    }
}

void LacSymQat_HashStatePrefixAadBufferSizeGet(
    icp_qat_la_bulk_req_ftr_t *pMsg,
    lac_sym_qat_hash_state_buffer_info_t *pHashStateBuf)
{
    const icp_qat_fw_auth_cd_ctrl_hdr_t *cd_ctrl;
    icp_qat_la_auth_req_params_t *pHashReqParams;

    LAC_ENSURE_NOT_NULL(pMsg);
    LAC_ENSURE_NOT_NULL(pHashStateBuf);

    cd_ctrl = (icp_qat_fw_auth_cd_ctrl_hdr_t *)&(pMsg->cd_ctrl);
    pHashReqParams =
        (icp_qat_la_auth_req_params_t *)(&(pMsg->serv_specif_rqpars));

    LAC_ENSURE_NOT_NULL(cd_ctrl);
    LAC_ENSURE_NOT_NULL(pHashReqParams);

    /* hash state storage needed to support partial packets. Space reserved
     * for this in all cases */
    pHashStateBuf->stateStorageSzQuadWords = LAC_BYTES_TO_QUADWORDS(
        sizeof(icp_qat_hw_auth_counter_t) + cd_ctrl->inner_state1_sz);

    pHashStateBuf->prefixAadSzQuadWords = pHashReqParams->hash_state_sz;
}

void LacSymQat_HashStatePrefixAadBufferPopulate(
    lac_sym_qat_hash_state_buffer_info_t *pHashStateBuf,
    icp_qat_la_bulk_req_ftr_t *pMsg,
    Cpa8U *pInnerPrefixAad,
    Cpa8U innerPrefixSize,
    Cpa8U *pOuterPrefix,
    Cpa8U outerPrefixSize)
{
    const icp_qat_fw_auth_cd_ctrl_hdr_t *cd_ctrl =
        (icp_qat_fw_auth_cd_ctrl_hdr_t *)&(pMsg->cd_ctrl);

    icp_qat_la_auth_req_params_t *pHashReqParams =
        (icp_qat_la_auth_req_params_t *)(&(pMsg->serv_specif_rqpars));

    LAC_ENSURE_NOT_NULL(pHashStateBuf);
    LAC_ENSURE_NOT_NULL(cd_ctrl);

    /*
     * Let S be the supplied secret
     * S1 = S/2 if S is even and (S/2 + 1) if S is odd.
     * Set length S2 (inner prefix) = S1 and the start address
     * of S2 is S[S1/2] i.e. if S is odd then S2 starts at the last byte of S1
     * _____________________________________________________________
     * |  outer prefix  |                padding                    |
     * |________________|                                           |
     * |                                                            |
     * |____________________________________________________________|
     * |  inner prefix  |                padding                    |
     * |________________|                                           |
     * |                                                            |
     * |____________________________________________________________|
     *
     */
    if (NULL != pInnerPrefixAad)
    {
        Cpa8U *pLocalInnerPrefix =
            (Cpa8U *)(pHashStateBuf->pData) +
            LAC_QUADWORDS_TO_BYTES(pHashStateBuf->stateStorageSzQuadWords);
        Cpa8U padding = pHashReqParams->u2.inner_prefix_sz - innerPrefixSize;
        /* copy the inner prefix or aad data */
        memcpy(pLocalInnerPrefix, pInnerPrefixAad, innerPrefixSize);

        /* Reset with zeroes any area reserved for padding in this block */
        if (0 < padding)
        {
            LAC_OS_BZERO(pLocalInnerPrefix + innerPrefixSize, padding);
        }
    }

    if (NULL != pOuterPrefix)
    {
        Cpa8U *pLocalOuterPrefix =
            (Cpa8U *)pHashStateBuf->pData +
            LAC_QUADWORDS_TO_BYTES(pHashStateBuf->stateStorageSzQuadWords +
                                   cd_ctrl->outer_prefix_offset);
        Cpa8U padding =
            LAC_QUADWORDS_TO_BYTES(pHashStateBuf->prefixAadSzQuadWords) -
            pHashReqParams->u2.inner_prefix_sz - outerPrefixSize;

        /* copy the outer prefix */
        memcpy(pLocalOuterPrefix, pOuterPrefix, outerPrefixSize);

        /* Reset with zeroes any area reserved for padding in this block */
        if (0 < padding)
        {
            LAC_OS_BZERO(pLocalOuterPrefix + outerPrefixSize, padding);
        }
    }
}

inline CpaStatus LacSymQat_HashRequestParamsPopulate(
    icp_qat_fw_la_bulk_req_t *pReq,
    Cpa32U authOffsetInBytes,
    Cpa32U authLenInBytes,
    sal_service_t *pService,
    lac_sym_qat_hash_state_buffer_info_t *pHashStateBuf,
    Cpa32U packetType,
    Cpa32U hashResultSize,
    CpaBoolean digestVerify,
    Cpa8U *pAuthResult,
    CpaCySymHashAlgorithm alg,
    void *pHKDFSecret)
{
    Cpa64U authResultPhys = 0;
    icp_qat_fw_la_auth_req_params_t *pHashReqParams;
#ifdef ICP_PARAM_CHECK
    LAC_ENSURE_NOT_NULL(pReq);
#endif

    pHashReqParams = (icp_qat_fw_la_auth_req_params_t
                          *)((Cpa8U *)&(pReq->serv_specif_rqpars) +
                             ICP_QAT_FW_HASH_REQUEST_PARAMETERS_OFFSET);
    /* For a full packet or last partial, we need to set the digest result
     * pointer */
    if (NULL != pAuthResult)
    {
        authResultPhys =
            LAC_OS_VIRT_TO_PHYS_EXTERNAL((*pService), (void *)pAuthResult);
        if (authResultPhys == 0)
        {
            LAC_LOG_ERROR("Unable to get the physical address of the"
                          " auth result\n");
            return CPA_STATUS_FAIL;
        }
    }
    pHashReqParams->auth_off = authOffsetInBytes;
    pHashReqParams->auth_len = authLenInBytes;

    /* Set the physical location of secret for HKDF */
    if (NULL != pHKDFSecret)
    {
        LAC_MEM_SHARED_WRITE_VIRT_TO_PHYS_PTR_EXTERNAL(
            (*pService), pHashReqParams->u1.aad_adr, pHKDFSecret);

        if (0 == pHashReqParams->u1.aad_adr)
        {
            LAC_LOG_ERROR("Unable to get the physical address of the"
                          " HKDF secret\n");
            return CPA_STATUS_FAIL;
        }
    }

    /* For a full packet or last partial, we need to set the auth result
     * field */
    pHashReqParams->auth_res_addr = authResultPhys;

    if (CPA_TRUE == digestVerify)
    {
        /* auth result size in bytes to be read in for a verify
         *  operation */
        pHashReqParams->auth_res_sz = (Cpa8U)hashResultSize;
    }
    else
    {
        pHashReqParams->auth_res_sz = 0;
    }

    /* If there is a hash state prefix buffer */
    if (NULL != pHashStateBuf)
    {
        /* Only write the pointer to the buffer if the size is greater than 0
         * this will be the case for plain and auth mode due to the
         * state storage required for partial packets and for nested mode (when
         * the prefix data is > 0) */
        if ((pHashStateBuf->stateStorageSzQuadWords +
             pHashStateBuf->prefixAadSzQuadWords) > 0)
        {
            /* For the first partial packet, the QAT expects the pointer to the
             * inner prefix even if there is no memory allocated for this. The
             * QAT will internally calculate where to write the state back. */
            if ((ICP_QAT_FW_LA_PARTIAL_START == packetType) ||
                (ICP_QAT_FW_LA_PARTIAL_NONE == packetType))
            {
                /* Prefix_addr changed to auth_partial_st_prefix */
                pHashReqParams->u1.auth_partial_st_prefix =
                    ((pHashStateBuf->pDataPhys) +
                     LAC_QUADWORDS_TO_BYTES(
                         pHashStateBuf->stateStorageSzQuadWords));
            }
            else
            {
                pHashReqParams->u1.auth_partial_st_prefix =
                    pHashStateBuf->pDataPhys;
            }
        }
        /* nested mode when the prefix data is 0 */
        else
        {
            pHashReqParams->u1.auth_partial_st_prefix = 0;
        }

        /* For middle & last partial, state size is the hash state storage
         * if hash mode 2 this will include the prefix data */
        if ((ICP_QAT_FW_LA_PARTIAL_MID == packetType) ||
            (ICP_QAT_FW_LA_PARTIAL_END == packetType))
        {
            pHashReqParams->hash_state_sz =
                (pHashStateBuf->stateStorageSzQuadWords +
                 pHashStateBuf->prefixAadSzQuadWords);
        }
        /* For full packets and first partials set the state size to that of
         * the prefix/aad. prefix includes both the inner and  outer prefix */
        else
        {
            pHashReqParams->hash_state_sz = pHashStateBuf->prefixAadSzQuadWords;
        }
    }
    else
    {
        pHashReqParams->u1.auth_partial_st_prefix = 0;
        pHashReqParams->hash_state_sz = 0;
    }

    /*  GMAC only */
    if (CPA_CY_SYM_HASH_AES_GMAC == alg)
    {
        pHashReqParams->hash_state_sz = 0;
        pHashReqParams->u1.aad_adr = 0;
    }

    /* This field is only used by TLS requests */
    /* In TLS case this is set after this function is called */
    pHashReqParams->resrvd1 = 0;
    return CPA_STATUS_SUCCESS;
}
